package org.example.system.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import lombok.RequiredArgsConstructor;
import org.example.system.converter.DictTypeConverter;
import org.example.system.mapper.SysDictTypeMapper;
import org.example.system.model.entity.SysDict;
import org.example.system.model.entity.SysDictType;
import org.example.system.model.form.DictTypeForm;
import org.example.system.model.query.DictTypePageQuery;
import org.example.system.model.vo.DictTypePageVO;
import org.example.system.service.SysDictService;
import org.example.system.service.SysDictTypeService;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * 数据字典类型业务实现类
 *
 * @author LY
 */
@Service
@RequiredArgsConstructor
public class SysDictTypeServiceImpl extends ServiceImpl<SysDictTypeMapper, SysDictType> implements SysDictTypeService {

    private final DictTypeConverter dictTypeConverter;

    private final SysDictService dictItemService;

    /**
     * 获取字典类型的分页列表
     * 该方法根据传入的分页查询参数，查询字典类型的分页列表，并将查询结果转换为分页视图对象返回。
     * 查询过程中会对分页参数进行校验和限制，确保分页参数在合理范围内。
     *
     * @param queryParams 分页查询对象，包含分页参数（如页码、每页大小）和查询关键字
     * @return 字典类型的分页列表，包含分页信息和字典类型数据
     * @throws RuntimeException 如果查询过程中发生异常，则抛出运行时异常
     */
    @Override
    public Page<DictTypePageVO> getDictTypePage(DictTypePageQuery queryParams) {
        try {
            // 参数获取与校验
            int pageNum = Math.max(1, queryParams.getPageNum());
            int pageSize = Math.min(100, Math.max(1, queryParams.getPageSize())); // 限制pageSize的最大值为100
            String keywords = queryParams.getKeywords();

            // 构建查询条件
            LambdaQueryWrapper<SysDictType> queryWrapper = buildQueryWrapper(keywords);

            // 查询数据
            Page<SysDictType> dictTypePage = this.page(new Page<>(pageNum, pageSize), queryWrapper);

            // 实体转换
            return dictTypeConverter.entity2Page(dictTypePage);
        } catch (Exception e) {
            throw new RuntimeException("查询字典分页列表失败", e);
        }
    }

    /**
     * 构建查询条件
     * 该方法用于根据传入的关键词构建一个查询条件包装器（LambdaQueryWrapper），
     * 用于查询SysDictType表中的数据。如果关键词不为空，则会在查询条件中添加
     * 对名称（name）和代码（code）字段的模糊匹配。
     *
     * @param keywords 查询关键词，用于在名称和代码字段中进行模糊匹配。如果为空，则不添加模糊匹配条件。
     * @return LambdaQueryWrapper<SysDictType> 返回构建好的查询条件包装器，包含查询条件和选择的字段。
     */
    private LambdaQueryWrapper<SysDictType> buildQueryWrapper(String keywords) {
        LambdaQueryWrapper<SysDictType> queryWrapper = new LambdaQueryWrapper<SysDictType>()
                .select(
                        SysDictType::getId,
                        SysDictType::getName,
                        SysDictType::getCode,
                        SysDictType::getStatus,
                        SysDictType::getRemark
                );

        if (StrUtil.isNotBlank(keywords)) {
            queryWrapper.and(wrapper -> wrapper
                    .like(SysDictType::getName, keywords)
                    .or()
                    .like(SysDictType::getCode, keywords));
        }

        return queryWrapper;
    }

    /**
     * 获取字典类型表单详情
     * 该方法根据传入的字典类型ID，查询并返回对应的字典类型表单详情。如果ID为空或字典类型不存在，将抛出异常。
     *
     * @param id 字典类型ID，不能为空
     * @return 字典类型表单详情
     * @throws IllegalArgumentException 如果字典类型ID为空或字典类型不存在
     * @throws RuntimeException 如果获取字典类型表单详情过程中发生其他异常
     */
    @Override
    public DictTypeForm getDictTypeForm(Long id) {
        try {
            // 检查id是否为null，若为空则抛出异常
            if (id == null) {
                throw new IllegalArgumentException("字典类型ID不能为空");
            }

            // 根据id查询字典类型实体，若实体不存在则抛出异常
            SysDictType entity = this.getOne(buildQueryWrapper(id));
            if (entity == null) {
                throw new IllegalArgumentException("字典类型不存在");
            }

            // 实体转换
            return dictTypeConverter.entity2Form(entity);
        } catch (Exception e) {
            throw new RuntimeException("获取字典类型表单详情失败", e);
        }
    }

    /**
     * 构建查询条件
     * 该方法用于根据字典类型ID构建一个LambdaQueryWrapper对象，用于查询SysDictType表的数据。
     * 查询条件包括ID的等值匹配，并且只选择特定的字段（ID、名称、代码、状态、备注）。
     *
     * @param id 字典类型ID，用于查询条件的等值匹配
     * @return LambdaQueryWrapper<SysDictType> 返回一个包含查询条件的LambdaQueryWrapper对象
     */
    private LambdaQueryWrapper<SysDictType> buildQueryWrapper(Long id) {
        return new LambdaQueryWrapper<SysDictType>()
                .eq(SysDictType::getId, id)
                .select(
                        SysDictType::getId,
                        SysDictType::getName,
                        SysDictType::getCode,
                        SysDictType::getStatus,
                        SysDictType::getRemark
                );
    }

    /**
     * 新增字典类型
     * 该方法用于将传入的字典类型表单数据转换为实体对象，并将其持久化到数据库中。
     *
     * @param dictTypeForm 字典类型表单，包含需要新增的字典类型信息
     * @return 是否新增成功，true表示新增成功，false表示新增失败
     */
    @Override
    public boolean saveDictType(DictTypeForm dictTypeForm) {
        // 将字典类型表单数据转换为实体对象
        SysDictType entity = dictTypeConverter.form2Entity(dictTypeForm);
        // 将实体对象持久化到数据库，并返回操作结果
        return this.save(entity);
    }

    /**
     * 修改字典类型
     *
     * @param id           字典类型ID，用于标识要修改的字典类型
     * @param dictTypeForm 字典类型表单，包含要更新的字典类型信息
     * @return 是否修改成功，true表示修改成功，false表示修改失败
     * @throws IllegalArgumentException 如果字典类型不存在，抛出此异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class) // 添加事务支持，保证一致性
    public boolean updateDictType(Long id, DictTypeForm dictTypeForm) {
        // 获取字典类型
        SysDictType sysDictType = this.getById(id);
        if (sysDictType == null) {
            throw new IllegalArgumentException("字典类型不存在");
        }

        // 将表单数据转换为实体对象
        SysDictType entity = dictTypeConverter.form2Entity(dictTypeForm);
        boolean result = this.updateById(entity);
        if (result) {
            String oldCode = sysDictType.getCode();
            String newCode = dictTypeForm.getCode();

            if (!StrUtil.equals(oldCode, newCode)) {
                // 如果字典类型编码发生变化，更新所有关联字典项的 typeCode
                dictItemService.update(new LambdaUpdateWrapper<SysDict>()
                        .eq(SysDict::getTypeCode, oldCode)
                        .set(SysDict::getTypeCode, newCode));
            }
        }
        return result;
    }

    /**
     * 删除字典类型
     * 该方法用于删除指定的字典类型。首先会验证传入的ID字符串是否有效，然后过滤掉非法的ID，
     * 接着查询这些ID对应的字典类型编码，并删除相关的字典项，最后删除字典类型本身。
     *
     * @param idsStr 字典类型ID，多个以英文逗号(,)分割
     * @return 是否删除成功
     * @throws IllegalArgumentException 如果传入的ID字符串为空或无效
     * @throws RuntimeException 如果删除过程中发生异常
     */
    @Override
    @Transactional(rollbackFor = Exception.class)
    public boolean deleteDictTypes(String idsStr) {
        try {
            if (StrUtil.isBlank(idsStr)) {
                throw new IllegalArgumentException("删除数据为空");
            }

            // 过滤非法ID
            List<Integer> ids = Arrays.stream(idsStr.split(","))
                    .map(String::trim)
                    .filter(s -> StrUtil.isNotEmpty(s) && s.matches("\\d+")) // 确保是数字字符串
                    .map(Integer::valueOf)
                    .distinct()
                    .collect(Collectors.toList());

            if (CollectionUtil.isEmpty(ids)) {
                throw new IllegalArgumentException("无效的字典类型ID");
            }

            // 查询这些ID对应的字典类型编码
            List<String> dictTypeCodes = this.listObjs(new LambdaQueryWrapper<SysDictType>()
                            .in(SysDictType::getId, ids)
                            .select(SysDictType::getCode),
                    Object::toString);

            if (CollectionUtil.isNotEmpty(dictTypeCodes)) {
                dictItemService.remove(new LambdaQueryWrapper<SysDict>()
                        .in(SysDict::getTypeCode, dictTypeCodes));
            }

            // 删除字典类型并返回结果
            return this.removeByIds(ids);
        } catch (Exception e) {
            throw new RuntimeException("删除字典类型失败", e);
        }
    }
}
